﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace EpiPoker.BusinessManagementObjects
{
    /// <summary>
    /// The class for determined the winner of the game
    /// </summary>
    public class Winner
    {
        #region Results

        /// <summary>
        /// Determine the winner of the game
        /// </summary>
        /// <param name="deck"></param>
        /// <param name="hands">The list of all players hands</param>
        /// <returns>The deck mixed</returns>
        public static DataAccess.T_User winner(List<Objects.Card> deck, List<Objects.Hand> hands)
        {
            List<Objects.Values> values = new List<Objects.Values>();

            for (int i = 0; i < hands.Count; i++)
            {
                values.Add(bestCombinaison(deck, hands[i]));
            }

            Dictionary<Objects.Hand, Objects.Values> winners_values = new Dictionary<Objects.Hand, Objects.Values>();

            for (int i = 9; i >= 1; i--)
			{
			    for (int j = 0; j < values.Count; j++)
			    {
			        if ((int)values[j] == i)
                        winners_values.Add(hands[j], values[j]);
			    }

                if (winners_values.Count != 0)
                    break;
			}

            int res = 0;
            List<Objects.Hand> winners = new List<Objects.Hand>();
            Objects.Values final = Objects.Values.Haute;

            foreach (var item in winners_values)
            {
                int tmp = valuePlayer(deck, item.Key, item.Value);
                if (tmp == res)
                    winners.Add(item.Key);
                else if (tmp > res)
                {
                    winners.Clear();
                    winners.Add(item.Key);
                    res = tmp;
                    final = item.Value;
                }
            }

            if (winners.Count > 1)               
                return egalite(winners, deck, final);
            else
                return winners[0].User;
        }

        /// <summary>
        /// Determine if there is an egality
        /// </summary>
        /// <param name="hands">All players Hands</param>
        /// <param name="cards">Flop + turn + river</param>
        /// <param name="value">Value of each cards in the deck</param>
        /// <returns>Boolean</returns>
        public static DataAccess.T_User egalite(List<Objects.Hand> hands, List<Objects.Card> cards, Objects.Values value)
        {
            if (((int)value == (int)Objects.Values.Couleur) || ((int)value == (int)Objects.Values.Flush)
                || ((int)value == (int)Objects.Values.Full) || ((int)value == (int)Objects.Values.Suite))
            {               
                DataAccess.T_User user = new DataAccess.T_User();
                return user;
            }
            else if ((int)value == (int)Objects.Values.Haute)
                return egaliteHaute(hands, cards);
            else if ((int)value == (int)Objects.Values.Paire)
                return egalitePaire(hands, cards);
            else if ((int)value == (int)Objects.Values.DoublePaire)
                return egaliteDoublePaire(hands, cards);
            else if ((int)value == (int)Objects.Values.Brelan)
                return egaliteBrelan(hands, cards);
            else if ((int)value == (int)Objects.Values.Carre)
                return egaliteCarre(hands, cards);

            return hands[0].User;
        }
        #endregion

        #region MathsResults

        /// <summary>
        /// Determine the best hand
        /// </summary>
        /// <param name="deck">Flop + turn + river</param>
        /// <param name="hand">The Hand of player</param>
        /// <returns>The value of his hand</returns>
        public static Objects.Values bestCombinaison(List<Objects.Card> deck, Objects.Hand hand)
        {
            List<Objects.Card> tmp = new List<Objects.Card>();

            for (int i = 0; i < deck.Count; i++)
            {
                Objects.Card c = new Objects.Card((int)deck[i].Color, (int)deck[i].Value);
                tmp.Add(c);
            }

            tmp.Add(new Objects.Card((int)hand.First.Color, (int)hand.First.Value));
            tmp.Add(new Objects.Card((int)hand.Second.Color, (int)hand.Second.Value));

            tmp.Sort(delegate(Objects.Card c1, Objects.Card c2)
            {
                return ((int)c1.Value).CompareTo((int)c2.Value);
            });

            if (Combinaison.isFlush(tmp))
                return Objects.Values.Flush;
            else if (Combinaison.isCarre(tmp))
                return Objects.Values.Carre;
            else if (Combinaison.isFull(tmp))
                return Objects.Values.Full;
            else if (Combinaison.isCouleur(tmp))
                return Objects.Values.Couleur;
            else if (Combinaison.isQuinte(tmp))
                return Objects.Values.Suite;
            else if (Combinaison.isBrelan(tmp))
                return Objects.Values.Brelan;
            else if (Combinaison.isDoublePaire(tmp))
                return Objects.Values.DoublePaire;
            else if (Combinaison.isPaire(tmp))
                return Objects.Values.Paire;
            else
                return Objects.Values.Haute;
        }

        /// <summary>
        /// Determine if there is an egality
        /// </summary>
        /// <param name="deck">Flop + turn + river</param>
        /// <param name="hand">The hand of player</param>
        /// <param name="value">Value of each cards in the deck</param>
        /// <returns>Integer who represent the value of his hand</returns>
        public static int valuePlayer(List<Objects.Card> deck, Objects.Hand hand, Objects.Values value)
        {
            List<Objects.Card> tmp = new List<Objects.Card>();

            for (int i = 0; i < deck.Count; i++)
            {
                Objects.Card c = new Objects.Card((int)deck[i].Color, (int)deck[i].Value);
                tmp.Add(c);
            }

            tmp.Add(new Objects.Card((int)hand.First.Color, (int)hand.First.Value));
            tmp.Add(new Objects.Card((int)hand.Second.Color, (int)hand.Second.Value));

            tmp.Sort(delegate(Objects.Card c1, Objects.Card c2)
            {
                return ((int)c1.Value).CompareTo((int)c2.Value);
            });

            if (value == Objects.Values.Haute)
                return (int)tmp[tmp.Count - 1].Value;
            else if (value == Objects.Values.Paire)
                return valuePaire(tmp);
            else if (value == Objects.Values.DoublePaire)
                return valueDoublePaire(tmp);
            else if (value == Objects.Values.Brelan)
                return valueBrelan(tmp);
            else if (value == Objects.Values.Suite)
                return valueQuinte(tmp);
            else if (value == Objects.Values.Couleur)
                return valueCouleur(tmp);
            else if (value == Objects.Values.Full)
                return valueFull(tmp);
            else if (value == Objects.Values.Carre)
                return valueCarre(tmp);
            else if (value == Objects.Values.Flush)
                return valueFlush(tmp);
            else
                return -1;
        }
        #endregion

        #region CombinaisonsValues

        /// <summary>
        /// Determine the value
        /// </summary>    
        /// <param name="value">Value of each cards in the deck</param>
        /// <returns>Integer who represent the value of the combinaison</returns>
        public static int valuePaire(List<Objects.Card> cards)
        {
            for (int i = 0; i < cards.Count - 1; i++)
            {
                if (cards[i].Value == cards[i + 1].Value)
                    return (int)cards[i].Value * (int)Objects.Values.Paire;
            }

            return -1;
        }

        /// <summary>
        /// Determine the value
        /// </summary>    
        /// <param name="value">Value of each cards in the deck</param>
        /// <returns>Integer who represent the value of the combinaison</returns>
        public static int valueBrelan(List<Objects.Card> cards)
        {
            for (int i = cards.Count - 1; i >= 2; i--)
            {
                if ((cards[i].Value == cards[i - 1].Value)
                    && (cards[i].Value == cards[i - 2].Value))
                    return (int)cards[i].Value * (int)Objects.Values.Brelan;
            }

            return -1;
        }

        /// <summary>
        /// Determine the value
        /// </summary>    
        /// <param name="value">Value of each cards in the deck</param>
        /// <returns>Integer who represent the value of the combinaison</returns>
        public static int valueDoublePaire(List<Objects.Card> cards)
        {
            int res = 0;
            int i = cards.Count - 1;
            int nb = 0;

            while (i > 0)
            {
                if (cards[i].Value == cards[i - 1].Value)
                {
                    res += (int)cards[i].Value * (int)Objects.Values.DoublePaire;
                    i = i - 2;
                    ++nb;
                }
                else
                    --i;

                if (nb == 2)
                    break;
            }

            return res;
        }

        /// <summary>
        /// Determine the value
        /// </summary>    
        /// <param name="value">Value of each cards in the deck</param>
        /// <returns>Integer who represent the value of the combinaison</returns>
        public static int valueQuinte(List<Objects.Card> cards)
        {
            int nb = 1;
            int res = 0;

            for (int i = 0; i < cards.Count - 1; i++)
            {
                if ((int)cards[i].Value == ((int)cards[i + 1].Value - 1))
                    ++nb;
                else
                    nb = 1;

                if (nb >= 5)
                    res = (int)cards[i + 1].Value * (int)Objects.Values.Suite;
            }

            return res;
        }

        /// <summary>
        /// Determine the value
        /// </summary>    
        /// <param name="value">Value of each cards in the deck</param>
        /// <returns>Integer who represent the value of the combinaison</returns>
        public static int valueCouleur(List<Objects.Card> cards)
        {
            int nb;
            int res = 0;

            for (int i = 0; i < 4; i++)
            {
                nb = 0;
                res = 0;

                for (int j = cards.Count - 1; j >= 0; j--)
                {
                    if (cards[j].Color == ((Objects.Color)i))
                    {
                        ++nb;
                        res += (int)cards[j].Value * (int)Objects.Values.Couleur;

                        if (nb >= 5)
                            break;
                    }
                }

                if (nb >= 5)
                    break;
            }

            return res;
        }

        /// <summary>
        /// Determine the value
        /// </summary>    
        /// <param name="value">Value of each cards in the deck</param>
        /// <returns>Integer who represent the value of the combinaison</returns>
        public static int valueFull(List<Objects.Card> cards)
        {
            int res = 0;
            int i = 0;

            while (i < cards.Count - 2)
            {
                if (((int)cards[i].Value == (int)cards[i + 1].Value)
                    && ((int)cards[i].Value == (int)cards[i + 2].Value))
                {
                    res += (int)cards[i].Value * (int)Objects.Values.Full * 3;
                    i = i + 3;
                }
                else if (((int)cards[i].Value == (int)cards[i + 1].Value)
                    && ((int)cards[i].Value != (int)cards[i + 2].Value))
                {
                    res += (int)cards[i].Value * (int)Objects.Values.Full * 2;
                    i = i + 2;
                }
                else
                    ++i;
            }

            if (((int)cards[5].Value == (int)cards[6].Value)
                && ((int)cards[5].Value != (int)cards[4].Value))
                res += (int)cards[5].Value * (int)Objects.Values.Full * 2;

            return res;
        }

        /// <summary>
        /// Determine the value
        /// </summary>    
        /// <param name="value">Value of each cards in the deck</param>
        /// <returns>Integer who represent the value of the combinaison</returns>
        public static int valueCarre(List<Objects.Card> cards)
        {
            for (int i = 0; i < cards.Count - 3; i++)
            {
                if (((int)cards[i].Value == (int)cards[i + 1].Value)
                    && ((int)cards[i].Value == (int)cards[i + 2].Value)
                    && ((int)cards[i].Value == (int)cards[i + 3].Value))
                    return (int)cards[i].Value * (int)Objects.Values.Carre;
            }

            return -1;
        }

        /// <summary>
        /// Determine the value
        /// </summary>    
        /// <param name="value">Value of each cards in the deck</param>
        /// <returns>Integer who represent the value of the combinaison</returns>
        public static int valueFlush(List<Objects.Card> cards)
        {
            int nb = 1;
            int res = 0;

            for (int i = 0; i < cards.Count - 1; i++)
            {
                if (((int)cards[i].Value == ((int)cards[i + 1].Value - 1))
                    && ((int)cards[i].Color == ((int)cards[i + 1].Color)))
                    ++nb;
                else
                    nb = 1;

                if (nb >= 5)
                    res = (int)cards[i + 1].Value * (int)Objects.Values.Flush;
            }

            return res;
        }

        /// <summary>
        /// Determine the value
        /// </summary>    
        /// <param name="hands">Hand of player</param>
        /// <param name="cards">Flop + turn + river</param>
        /// <returns>T_User who represent the value of the egality</returns>
        public static DataAccess.T_User egaliteHaute(List<Objects.Hand> hands, List<Objects.Card> cards)
        {
            List<Objects.Card> tmp = sort(hands[0], cards);
            List<Objects.Hand> gagnants = new List<Objects.Hand>();
            bool first = false;
            bool second = false;

            tmp.RemoveAt(0);
            tmp.RemoveAt(0);

            gagnants.Add(hands[0]);

            for (int i = 1; i < hands.Count; i++)
            {
                List<Objects.Card> tmp2 = sort(hands[i], cards);

                tmp2.RemoveAt(0);
                tmp2.RemoveAt(0);

                for (int j = tmp.Count - 1; j >= 0; j--)
                {
                    if ((int)tmp[j].Value > (int)tmp2[j].Value)
                    {
                        first = true;
                        break;
                    }
                    else if ((int)tmp[j].Value < (int)tmp2[j].Value)
                    {
                        second = true;
                        break;
                    }
                }

                if (first)
                    first = false;
                else if (second)
                {
                    tmp = tmp2;
                    gagnants.Clear();
                    gagnants.Add(hands[i]);
                    second = false;
                }
                else
                    gagnants.Add(hands[i]);
            }

            if (gagnants.Count > 1)
            {
                DataAccess.T_User user = new DataAccess.T_User();
                return user;
            }
                      
            return gagnants[0].User;
        }

        /// <summary>
        /// Determine the value
        /// </summary>    
        /// <param name="hands">Hand of player</param>
        /// <param name="cards">Flop + turn + river</param>
        /// <returns>T_User who represent the value of the egality</returns>
        public static DataAccess.T_User egalitePaire(List<Objects.Hand> hands, List<Objects.Card> cards)
        {
            List<Objects.Card> tmp = sort(hands[0], cards);
            List<Objects.Hand> gagnants = new List<Objects.Hand>();
            int reference = 0;
            bool first = false;
            bool second = true;

            for (int i = 0; i < tmp.Count - 1; i++)
			{
			    if ((int)tmp[i].Value == (int)tmp[i + 1].Value)
                {
                    reference = i;
                    break;
                }
			}

            tmp.RemoveAt(reference);
            tmp.RemoveAt(reference);
            tmp.RemoveAt(0);
            tmp.RemoveAt(0);

            gagnants.Add(hands[0]);

            for (int i = 1; i < hands.Count; i++)
			{
			    List<Objects.Card> tmp2 = sort(hands[i], cards);

                for (int j = 0; j < tmp2.Count - 1; j++)
			    {
			        if ((int)tmp2[j].Value == (int)tmp2[j + 1].Value)
                    {
                        reference = j;
                        break;
                    }
			    }

                tmp2.RemoveAt(reference);
                tmp2.RemoveAt(reference);
                tmp2.RemoveAt(0);
                tmp2.RemoveAt(0);

                for (int j = tmp.Count - 1; j >= 0; j--)
                {
                    if ((int)tmp[j].Value > (int)tmp2[j].Value)
                    {
                        first = true;
                        break;
                    }
                    else if ((int)tmp[j].Value < (int)tmp2[j].Value)
                    {
                        second = true;
                        break;
                    }
                }

                if (first)
                    first = false;
                else if (second)
                {
                    tmp = tmp2;
                    gagnants.Clear();
                    gagnants.Add(hands[i]);
                    second = false;
                }
                else
                    gagnants.Add(hands[i]);
			}

            if (gagnants.Count > 1)
            {
                DataAccess.T_User user = new DataAccess.T_User();
                return user;
            }

            return gagnants[0].User;
        }

        /// <summary>
        /// Determine the value
        /// </summary>    
        /// <param name="hands">Hand of player</param>
        /// <param name="cards">Flop + turn + river</param>
        /// <returns>T_User who represent the value of the egality</returns>
        public static DataAccess.T_User egaliteDoublePaire(List<Objects.Hand> hands, List<Objects.Card> cards)
        {
            List<Objects.Card> tmp = sort(hands[0], cards);
            List<Objects.Hand> gagnants = new List<Objects.Hand>();
            int reference = 0;

            for (int i = tmp.Count - 1; i >= 1; i--)
            {
                if (((int)tmp[i].Value == (int)tmp[i - 1].Value))
                {
                    reference = i - 1;
                    break;
                }
            }

            tmp.RemoveAt(reference);
            tmp.RemoveAt(reference);

            for (int i = tmp.Count - 1; i >= 1; i--)
            {
                if (((int)tmp[i].Value == (int)tmp[i - 1].Value))
                {
                    reference = i - 1;
                    break;
                }
            }

            tmp.RemoveAt(reference);
            tmp.RemoveAt(reference);
            tmp.RemoveAt(0);
            tmp.RemoveAt(0);

            gagnants.Add(hands[0]);

            for (int i = 1; i < hands.Count; i++)
            {
                List<Objects.Card> tmp2 = sort(hands[i], cards);

                for (int j = tmp2.Count - 1; j >= 1; j--)
                {
                    if (((int)tmp2[j].Value == (int)tmp2[j - 1].Value))
                    {
                        reference = j - 1;
                        break;
                    }
                }

                tmp2.RemoveAt(reference);
                tmp2.RemoveAt(reference);

                for (int j = tmp2.Count - 1; j >= 1; j--)
                {
                    if (((int)tmp2[j].Value == (int)tmp2[j - 1].Value))
                    {
                        reference = j - 1;
                        break;
                    }
                }

                tmp2.RemoveAt(reference);
                tmp2.RemoveAt(reference);
                tmp2.RemoveAt(0);
                tmp2.RemoveAt(0);

                if ((int)tmp[0].Value < (int)tmp2[0].Value)
                {
                    tmp = tmp2;
                    gagnants.Clear();
                    gagnants.Add(hands[i]);
                }
                else if ((int)tmp[0].Value == (int)tmp2[0].Value)
                    gagnants.Add(hands[i]);
            }

            if (gagnants.Count > 1)
            {
                DataAccess.T_User user = new DataAccess.T_User();
                return user;
            }
                        
            return gagnants[0].User;
        }

        /// <summary>
        /// Determine the value
        /// </summary>    
        /// <param name="hands">Hand of player</param>
        /// <param name="cards">Flop + turn + river</param>
        /// <returns>T_User who represent the value of the egality</returns>
        public static DataAccess.T_User egaliteBrelan(List<Objects.Hand> hands, List<Objects.Card> cards)
        {
            List<Objects.Card> tmp = sort(hands[0], cards);
            List<Objects.Hand> gagnants = new List<Objects.Hand>();
            int reference = 0;
            bool first = false;
            bool second = true;

            for (int i = tmp.Count - 1; i >= 2; i--)
            {
                if (((int)tmp[i].Value == (int)tmp[i - 1].Value)
                    && ((int)tmp[i].Value == (int)tmp[i - 2].Value))
                {
                    reference = i - 2;
                    break;
                }
            }

            tmp.RemoveAt(reference);
            tmp.RemoveAt(reference);
            tmp.RemoveAt(reference);
            tmp.RemoveAt(0);
            tmp.RemoveAt(0);

            gagnants.Add(hands[0]);

            for (int i = 1; i < hands.Count; i++)
            {
                List<Objects.Card> tmp2 = sort(hands[i], cards);

                for (int j = tmp2.Count - 1; j >= 2; j--)
                {
                    if (((int)tmp2[j].Value == (int)tmp2[j - 1].Value)
                        && ((int)tmp2[j].Value == (int)tmp2[j - 2].Value))
                    {
                        reference = j - 2;
                        break;
                    }
                }

                tmp2.RemoveAt(reference);
                tmp2.RemoveAt(reference);
                tmp2.RemoveAt(reference);
                tmp2.RemoveAt(0);
                tmp2.RemoveAt(0);

                for (int j = tmp.Count - 1; j >= 0; j--)
                {
                    if ((int)tmp[j].Value > (int)tmp2[j].Value)
                    {
                        first = true;
                        break;
                    }
                    else if ((int)tmp[j].Value < (int)tmp2[j].Value)
                    {
                        second = true;
                        break;
                    }
                }

                if (first)
                    first = false;
                else if (second)
                {
                    tmp = tmp2;
                    gagnants.Clear();
                    gagnants.Add(hands[i]);
                    second = false;
                }
                else
                    gagnants.Add(hands[i]);
            }

            if (gagnants.Count > 1)
            {
                DataAccess.T_User user = new DataAccess.T_User();
                return user;
            }

           
            return gagnants[0].User;
        }

        /// <summary>
        /// Determine the value
        /// </summary>    
        /// <param name="hands">Hand of player</param>
        /// <param name="cards">Flop + turn + river</param>
        /// <returns>T_User who represent the value of the egality</returns>
        public static DataAccess.T_User egaliteCarre(List<Objects.Hand> hands, List<Objects.Card> cards)
        {
            List<Objects.Card> tmp = sort(hands[0], cards);
            List<Objects.Hand> gagnants = new List<Objects.Hand>();
            int reference = 0;

            for (int i = 0; i < tmp.Count - 3; i++)
            {
                if (((int)tmp[i].Value == (int)tmp[i + 1].Value)
                    && ((int)tmp[i].Value == (int)tmp[i + 2].Value)
                    && ((int)tmp[i].Value == (int)tmp[i + 3].Value))
                {
                    reference = i;
                    break;
                }
            }

            tmp.RemoveAt(reference);
            tmp.RemoveAt(reference);
            tmp.RemoveAt(reference);
            tmp.RemoveAt(reference);
            tmp.RemoveAt(0);
            tmp.RemoveAt(0);

            gagnants.Add(hands[0]);

            for (int i = 1; i < hands.Count; i++)
            {
                List<Objects.Card> tmp2 = sort(hands[i], cards);

                for (int j = 0; j < tmp2.Count - 3; j++)
                {
                    if (((int)tmp2[j].Value == (int)tmp2[j + 1].Value)
                        && ((int)tmp2[j].Value == (int)tmp2[j + 2].Value)
                        && ((int)tmp2[j].Value == (int)tmp2[j + 3].Value))
                    {
                        reference = j;
                        break;
                    }
                }

                tmp2.RemoveAt(reference);
                tmp2.RemoveAt(reference);
                tmp2.RemoveAt(reference);
                tmp2.RemoveAt(reference);
                tmp2.RemoveAt(0);
                tmp2.RemoveAt(0);

                if ((int)tmp[0].Value < (int)tmp2[0].Value)
                {
                    tmp = tmp2;
                    gagnants.Clear();
                    gagnants.Add(hands[i]);
                }
                else if ((int)tmp[0].Value == (int)tmp2[0].Value)
                    gagnants.Add(hands[i]);
            }

            if (gagnants.Count > 1)
            {
                DataAccess.T_User user = new DataAccess.T_User();
                return user;
            }

            return gagnants[0].User;
        }

        /// <summary>
        /// Sort the cards 
        /// </summary>    
        /// <param name="hands">Hand of player</param>
        /// <param name="cards">Flop + turn + river</param>
        /// <returns>A list of Cards sorted</returns>
        private static List<Objects.Card> sort(Objects.Hand hand, List<Objects.Card> cards)
        {
            List<Objects.Card> tmp = new List<Objects.Card>();

            for (int i = 0; i < cards.Count; i++)
            {
                Objects.Card c = new Objects.Card((int)cards[i].Color, (int)cards[i].Value);
                tmp.Add(c);
            }

            tmp.Add(new Objects.Card((int)hand.First.Color, (int)hand.First.Value));
            tmp.Add(new Objects.Card((int)hand.Second.Color, (int)hand.Second.Value));

            tmp.Sort(delegate(Objects.Card c1, Objects.Card c2)
            {
                return ((int)c1.Value).CompareTo((int)c2.Value);
            });

            return tmp;
        }
        #endregion
    }
}